﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Threading;
using SimpleLibrary.Convertors;
using SimpleLibrary.Components;
using System.Windows.Media;
using System.Diagnostics;

namespace SimpleLibrary
{
	/// <summary>
	/// Interaction logic for MainScreen.xaml
	/// </summary>
	public partial class MainScreen : Window
	{
        //Constant which indicate the time duration each book is lended for.
        private const Int32 MaxDuration = 3;
	    private DataView _bookList = null;
        private readonly Timer _timer;
	    public static MainScreen Instance;
		public MainScreen()
		{
			this.InitializeComponent();
		    Instance = this;

		    lblGreen.Background = Brushes.Green;
		    lblRed.Background = Brushes.Red;
		    lblYellow.Background = Brushes.Yellow;
		    lblYellowGreen.Background = Brushes.YellowGreen;

            //Timer for showing the current time on the window.
            _timer = new Timer(TimerTick, null, new TimeSpan(0, 0, 1), new TimeSpan(0, 0, 1));
            
            //Loading the books and users from the local data.
		    Logger.WriteTrace("Loading the data", TraceLevel.Verbose);
            LoadUserListInBookScreen();
            LoadBookListInUserScreen();

		    LoadUserList();
		    LoadBookList();
            Logger.WriteTrace("Loading completed",TraceLevel.Verbose);
        }
        #region Loading
        //binding is used to bind the data with the listbox using convertors.
        private void LoadBookListInUserScreen()
        {
            cbBooks1.ItemsSource = null;
            cbBooks1.Items.Clear();
           
            DataView view = DataManager.Instance.GetAllBooks();
            if (view == null) return;
            view.AddNew();
            Binding binding = new Binding
            {
                Source = view,
                Mode = BindingMode.OneWay,
                Converter = new BookComboConvertor()
            };

            cbBooks1.SetBinding(ItemsControl.ItemsSourceProperty, binding);

            cbBooks2.ItemsSource = null;
            cbBooks2.Items.Clear();

            DataView view2 = DataManager.Instance.GetAllBooks();
            if (view2 == null) return;
            view2.AddNew();
            Binding binding1 = new Binding
            {
                Source = view,
                Mode = BindingMode.OneWay,
                Converter = new BookComboConvertor()
            };

            cbBooks2.SetBinding(ItemsControl.ItemsSourceProperty, binding1);
        }

        private void LoadUserListInBookScreen()
        {
            cbUsers.ItemsSource = null;
            cbUsers.Items.Clear();

            DataView view = DataManager.Instance.GetAllUsers();
            if (view == null) return;
            view.AddNew();
            Binding binding = new Binding
            {
                Source = view,
                Mode = BindingMode.OneWay,
                Converter = new UserComboConvertor()
            };

            cbUsers.SetBinding(ItemsControl.ItemsSourceProperty, binding);
        }

	    private void LoadBookList()
	    {
            Logger.WriteTrace("Loading the Book list",TraceLevel.Info);
            lstBooks.ItemsSource = null;
            lstBooks.Items.Clear();

            DataView view = DataManager.Instance.GetAllBooks();
            if (view == null) return;
            _bookList = view;
            Binding binding = new Binding
            {
                Source = view,
                Mode = BindingMode.OneWay,
                Converter = new BookConvertor()
            };

            lstBooks.SetBinding(ItemsControl.ItemsSourceProperty, binding);
	    }

	    private void LoadUserList()
	    {
            Logger.WriteTrace("Loading the user list", TraceLevel.Info);
            lstUsers.ItemsSource = null;
            lstUsers.Items.Clear();

            DataView view = DataManager.Instance.GetAllUsers();
            if (view == null) return;
            _bookList = view;
            Binding binding = new Binding
            {
                Source = view,
                Mode = BindingMode.OneWay,
                Converter = new UserConvertor()
            };

            lstUsers.SetBinding(ItemsControl.ItemsSourceProperty, binding);
        }
        #endregion

        #region Time Update
        private void TimerTick(object sender)
        {
            TimeZone sysTimeZoneInfo = TimeZone.CurrentTimeZone;

            string strAbbreviatedTomeZone = sysTimeZoneInfo.StandardName;
            char[] delimiterChars = { ' ' };

            string[] strTimeZoneSplit = strAbbreviatedTomeZone.Split(delimiterChars);
            strAbbreviatedTomeZone = "";
            for (int intLoop = 0; intLoop < strTimeZoneSplit.Length; intLoop++)
            {
                //strTimeZoneSplit[intLoop] = strTimeZoneSplit[intLoop].Substring(0, 1);
                strTimeZoneSplit[intLoop] = strTimeZoneSplit[intLoop][0].ToString();

                if (strTimeZoneSplit[intLoop][0].Equals('('))
                    strTimeZoneSplit[intLoop] = strTimeZoneSplit[intLoop][1].ToString();

                strAbbreviatedTomeZone = strAbbreviatedTomeZone + strTimeZoneSplit[intLoop];
            }

            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<String>(UpdateTime), strAbbreviatedTomeZone);
            //UpdateTime(strAbbreviatedTomeZone);

        }

        private void UpdateTime(string strAbbreviatedTomeZone)
        {
            lblSysTime.Content = String.Format("\n{0}\n{1} {2}", DateTime.Now.ToLongDateString(),
                                               DateTime.Now.ToString("HH:mm:ss"), strAbbreviatedTomeZone);
        }
        #endregion

        /// <summary>
        /// Event trigger when a book selection is changed. 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private void BookSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if(sender == null) return;
            BookListItem book = lstBooks.SelectedItem as BookListItem;
            Logger.WriteTrace("Book Selection Changed", TraceLevel.Verbose);
            if(book != null)
            {
                txtTitle.Text = book.BookName;
                txtAuthor.Text = book.Authour;
                txtPublisher.Text = book.Publisher;
                txtyear.Text = book.Year;
                cbUsers.SelectedIndex = cbUsers.Items.Count - 1;

                MakeReadOnly(Tabs.Book, true);

                //Used to update the status of the book. whether avaiable or lended to some user.
                Int64 userId = DbDriver.Instance.GetPresentStatus(book.ID);
                if(userId == -1)
                {
                    lblCurrentStatus.Content = "Available";
                    btnBookreturn.IsEnabled = false;
                    cbUsers.IsEnabled = true;
                    cbUsers.SelectedIndex = cbUsers.Items.Count - 1;
                    btnUpdate.IsEnabled = true;
                }
                else
                {
                    lblCurrentStatus.Content = DataManager.Instance.GetNameFromId(userId.ToString());
                    btnBookreturn.IsEnabled = true;
                    cbUsers.IsEnabled = false;
                    btnUpdate.IsEnabled = false;
                }
            }
        }

        /// <summary>
        /// Event trigger when user selection is changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (sender == null) return;
            UserListItem user = lstUsers.SelectedItem as UserListItem;
            Logger.WriteTrace("User selection Changed", TraceLevel.Verbose);
            if (user != null)
            {
                txtName.Text = user.UserName;
                txtPhone.Text = user.PhoneNo;
                txtMailId.Text = user.MailId;
                txtAddress.Text = user.Address;

                MakeReadOnly(Tabs.User,true);

                //pending books for the user is retrieved and showed.
                List<Int32> bookList = DbDriver.Instance.GetpendingBooks(user.ID);

                if(bookList.Count == 0)
                {
                    //No books pending.
                    cbBooks1.SelectedIndex = cbBooks1.Items.Count - 1;
                    cbBooks2.SelectedIndex = cbBooks2.Items.Count - 1;
                    //update the user status to green
                    lblUserStatus.Background = Brushes.Green;

                    cbBooks1.IsEnabled = true;
                    cbBooks2.IsEnabled = true;
                    btnUserReturn1.Content = "Update";
                    btnUserReturn2.Content = "Update";
                }
                else
                {
                    if(bookList.Count == 1)
                    {
                        //One book lended and he can borrow another book.
                        cbBooks2.SelectedIndex = cbBooks2.Items.Count - 1;

                        cbBooks2.IsEnabled = true;
                        btnUserReturn2.Content = "Update";

                        cbBooks1.SelectedItem = FindIndex(bookList[0],cbBooks1.Items);
                        cbBooks1.IsEnabled = false;
                        btnUserReturn1.Content = "Return";
                    }
                    else if(bookList.Count == 2)
                    {
                        //No books pending.
                        cbBooks1.SelectedItem = FindIndex(bookList[0], cbBooks1.Items);
                        cbBooks2.SelectedItem = FindIndex(bookList[1], cbBooks2.Items);
                        
                        cbBooks1.IsEnabled = false;
                        cbBooks2.IsEnabled = false;
                        btnUserReturn1.Content = "Return";
                        btnUserReturn2.Content = "Return";
                    }

                    try
                    {
                        //past history of the user is validated.
                        if (DbDriver.Instance.IsValidUser(user.ID))
                        {
                            //One more book.
                            lblUserStatus.Background = Brushes.YellowGreen;
                        }
                        else
                        {
                            // No more books.
                            lblUserStatus.Background = Brushes.Yellow;
                        }
                    }
                    catch (BadHistoryUser)
                    {
                        //need to return the book as it is already overdue.
                        lblUserStatus.Background = Brushes.Red;
                    }
                }
            }
        }
        /// <summary>
        /// Private function to find the index of the item based on the ID.
        /// </summary>
        /// <param name="bookId"></param>
        /// <param name="bookCollection"></param>
        /// <returns></returns>
        private static object FindIndex(int bookId, ItemCollection bookCollection)
        {
            foreach (BookComboItem item in bookCollection)
            {
                if(bookId.ToString().Equals(item.ID))
                {
                    return item;
                }
            }
            return bookCollection[bookCollection.Count - 1];
        }
        /// <summary>
        /// Private function to make controls readonly based on paramter passed.
        /// </summary>
        /// <param name="tab"></param>
        /// <param name="isReadOnly"></param>
        private void MakeReadOnly(Tabs tab,bool isReadOnly)
        {
            if(tab == Tabs.Book)
            {
                txtTitle.IsReadOnly = isReadOnly;
                txtAuthor.IsReadOnly = isReadOnly;
                txtPublisher.IsReadOnly = isReadOnly;
                txtyear.IsReadOnly = isReadOnly;
            }
            else
            {
                txtName.IsReadOnly = true;
                txtPhone.IsReadOnly = isReadOnly;
                txtMailId.IsReadOnly = isReadOnly;
                txtAddress.IsReadOnly = isReadOnly;
                btnEditUser.IsEnabled = isReadOnly;
            }
        }

        /// <summary>
        /// Event for button click event of Edit user.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditUserClicked(object sender, RoutedEventArgs e)
        {
            if (btnEditUser.Content.ToString().Equals("Edit"))
            {
                Logger.WriteTrace("Editing the user", TraceLevel.Verbose);
                //Going to edit already added user.
                MakeReadOnly(Tabs.User, false);
                btnEditUser.IsEnabled = true;
                btnEditUser.Content = "Save";
                btnEditUser.Tag = null;
            }
            else
            {
                //Save the changes to data row and also to database.
                btnEditUser.Content = "Edit";
                MakeReadOnly(Tabs.User,true);

                if (btnEditUser.Tag == null)
                {
                    //Edited already available user.
                    UserListItem user = lstUsers.SelectedItem as UserListItem;
                    if (user != null)
                    {
                        Logger.WriteTrace("Updating the user Information", TraceLevel.Verbose);
                        DbDriver.Instance.UpdateUserDetails(user.ID, user.PhoneNo, user.Address, user.MailId);
                    }
                }
                else
                {
                    //Add New user
                    if(!String.IsNullOrEmpty(txtName.Text))
                    {
                        Logger.WriteTrace("Adding a new user", TraceLevel.Verbose);
                        DbDriver.Instance.InsertNewUser(String.Empty,txtName.Text, txtPhone.Text, txtAddress.Text, txtMailId.Text);
                    }
                    else
                    {
                        MessageBox.Show("User Name can not be empty.", "Error");
                    }
                }
                ReloadUserList();
            }
        }

        /// <summary>
        /// Return book click event from the book screen,
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReturnBookClicked(object sender, RoutedEventArgs e)
        {
            BookListItem book = lstBooks.SelectedItem as BookListItem;
            if (book != null)
            {
                String id = book.ID;
                Logger.WriteTrace("Book with Id " + id + " returned", TraceLevel.Info);
                bool status = DbDriver.Instance.ReturnBook(id);

                if(status)
                {
                    lblCurrentStatus.Content = "Available";
                    btnBookreturn.IsEnabled = false;
                    cbUsers.IsEnabled = true;

                    LoadUserList();
                    LoadBookList();
                }
            }
        }

        private void LendToSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            btnUpdate.IsEnabled = true;
        }

        /// <summary>
        /// Lend is clicked on the Book screen.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LendClicked(object sender, RoutedEventArgs e)
        {
            if (cbUsers.SelectedItem == null)
                return;
            BookListItem book = lstBooks.SelectedItem as BookListItem;
            if (book != null)
            {
                btnUpdate.IsEnabled = false;
                string id = book.ID;
                if (cbUsers.SelectedItem.GetType().Equals(typeof(UserComboItem)))
                {
                    String userId =
                        DataManager.Instance.GetIdFromName(((ComboBoxItem)cbUsers.SelectedItem).Content.ToString());
                    bool isValidUser = false;
                    bool havingBadHistory = false;
                    try
                    {
                        // Checking the user history before lending.
                        isValidUser = DbDriver.Instance.IsValidUser(userId);
                    }
                    catch (BadHistoryUser)
                    {
                        havingBadHistory = true;
                    }
                    

                    if (isValidUser)
                    {
                        if (String.IsNullOrEmpty(userId))
                            return;
                        Logger.WriteTrace("Book with ID " + id + " lended to user " + userId + " for " + MaxDuration, TraceLevel.Info);
                        DbDriver.Instance.LendBook(id, userId, MaxDuration);

                        LoadBookList();
                        LoadUserList();
                    }
                    else
                    {
                        if(havingBadHistory)
                        {
                            MessageBox.Show(
                                "User is having bad history. Need to return pending books before borrowing anymore.",
                                "Bad History", MessageBoxButton.OK);
                        }
                        else
                        {
                            MessageBox.Show("User already reached his limit of 2 Books.", "Limit Reached.",
                                            MessageBoxButton.OK);
                        }
                        cbUsers.SelectedIndex = cbUsers.Items.Count - 1;    
                        
                    }
                   
                }
            }
        }

        /// <summary>
        /// Add new book button click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AddBookClicked(object sender, RoutedEventArgs e)
        {
            //Clear the fields and read only false so that user can enter the values.
            ClearFields(Tabs.Book);
            MakeReadOnly(Tabs.Book,false);
            btnSaveBook.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// Private function to cleat the fields based on parameters.
        /// </summary>
        /// <param name="tabs"></param>
        private void ClearFields(Tabs tabs)
        {
            if(tabs == Tabs.Book)
            {
                txtTitle.Clear();
                txtPublisher.Clear();
                txtAuthor.Clear();
                txtyear.Clear();
            }
            else
            {
                txtName.Clear();
                txtMailId.Clear();
                txtPhone.Clear();
                txtAddress.Clear();
            }
        }

        /// <summary>
        /// New book save clicked event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveBookCllicked(object sender, RoutedEventArgs e)
        {
            MakeReadOnly(Tabs.Book,true);
            btnSaveBook.Visibility = Visibility.Collapsed;
            if (String.IsNullOrEmpty(txtTitle.Text.Trim()))
            {
                //validation to make sure title is not empty.
                MessageBox.Show("Title can not be Empty");
                return;
            }
            DbDriver.Instance.InsertNewBook(txtTitle.Text, txtAuthor.Text, txtPublisher.Text, txtyear.Text);

            ReloadBookList();

        }

        /// <summary>
        /// Return of book clicked on the user screen.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserReturnClicked(object sender, RoutedEventArgs e)
        {
            ComboBox box = null;
            var button = sender as Button;
            if(button == null)return;
            //Checking whether it means return or update,
            if (button.Content.ToString().Equals("Return"))
            {
                BookComboItem book = null;
                //detecting which combo book is being returned.
                if (sender.Equals(btnUserReturn1))
                {
                    book = cbBooks1.SelectedItem as BookComboItem;
                    box = cbBooks1;
                }
                else if (sender.Equals(btnUserReturn2))
                {
                    book = cbBooks2.SelectedItem as BookComboItem;
                    box = cbBooks2;
                }
                if (book != null)
                {
                    String id = book.ID;
                    bool status = DbDriver.Instance.ReturnBook(id);
                    button.Content = "Update";
                    box.IsEnabled = true;
                    box.SelectedIndex = box.Items.Count - 1;

                    LoadBookList();
                }
            }
            else if(button.Content.ToString().Equals("Update"))
            {
                
                BookComboItem book = null;
                //Detecting the combobox which needs to be processed.
                if (sender.Equals(btnUserReturn1))
                {
                    book = cbBooks1.SelectedItem as BookComboItem;
                    box = cbBooks1;
                }
                else if (sender.Equals(btnUserReturn2))
                {
                    book = cbBooks2.SelectedItem as BookComboItem;
                    box = cbBooks2;
                }
                if(book != null)
                {
                    string id = book.ID;
                    
                    string userId = ((UserListItem) lstUsers.SelectedItem).ID;
                    if (!String.IsNullOrEmpty(userId) && !string.IsNullOrEmpty(id))
                    {
                        //Checking whether the book is available.
                        if (DbDriver.Instance.IsBookAvailable(id))
                        {
                            box.SelectedItem = FindIndex(Int32.Parse(book.ID), box.Items);
                            DbDriver.Instance.LendBook(id, userId, MaxDuration);
                            box.IsEnabled = false;
                            button.Content = "Return";

                            LoadBookList();
                            UserSelectionChanged(lstUsers,null);
                        }
                        else
                        {
                            MessageBox.Show("Book is already lended. Not available.", "Limit Reached.",
                                      MessageBoxButton.OK);
                            box.SelectedIndex = box.Items.Count - 1;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Click event of Add user.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AddUserClicked(object sender, RoutedEventArgs e)
        {
            ClearFields(Tabs.User);
            MakeReadOnly(Tabs.User, false);
            btnEditUser.Content = "Save";
            btnEditUser.Tag = 1;
            txtName.IsReadOnly = false;
            btnEditUser.IsEnabled = true;
        }

        /// <summary>
        /// Click event for deleting user.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserDeleteClicked(object sender, RoutedEventArgs e)
        {
            if(lstUsers.SelectedIndex == -1) return;
            UserListItem item = lstUsers.SelectedItem as UserListItem;
            if(item != null)
            {
                MessageBoxResult result = MessageBox.Show("Are you sure you want to delete the user?", "Confirmation", MessageBoxButton.YesNo);
                if(result == MessageBoxResult.Yes)
                {
                    DbDriver.Instance.DeleteUser(item.ID);
                    ReloadUserList();
                    MessageBox.Show("User Successfully deleted.", "Success");
                }
            }
        }

        /// <summary>
        /// Reloading the user data in the screen to keep them up-to date.
        /// </summary>
	    private void ReloadUserList()
	    {
            DataManager.Instance.Init();
            Thread.Sleep(100);

            LoadUserListInBookScreen();
            LoadUserList();
	    }


        /// <summary>
        /// Click event for deleting book.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
	    private void BookDeleteClicked(object sender, RoutedEventArgs e)
        {
            if (lstBooks.SelectedIndex == -1) return;
            BookListItem item = lstBooks.SelectedItem as BookListItem;
            if (item != null)
            {
                MessageBoxResult result = MessageBox.Show("Are you sure you want to delete the Book?", "Confirmation", MessageBoxButton.YesNo);
                if (result == MessageBoxResult.Yes)
                {
                    DbDriver.Instance.DeleteBook(item.ID);
                    ReloadBookList();
                    MessageBox.Show("Book Successfully deleted.", "Success");
                }
            }
        }

        /// <summary>
        /// Reloading the book data in the screen to keep them up-to date.
        /// </summary>
        private void ReloadBookList()
        {
            DataManager.Instance.Init();
            Thread.Sleep(100);

            LoadBookListInUserScreen();
            LoadBookList();
        }

        /// <summary>
        /// Generate report click event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GenerateReportClicked(object sender, RoutedEventArgs e)
        {
            List<String> data = null;
            String reportName = String.Empty;
            Reports.Reports report = Reports.Reports.AvailableBooks;
            if(lstReports.SelectedIndex == -1)
                return;
           if(lstReports.SelectedItem.Equals(lbtAvailableBooks))
           {
               data = DbDriver.Instance.GetAllAvailableBooks();
               reportName = "Available Books";
               report = Reports.Reports.AvailableBooks;
           }
           else if(lstReports.SelectedItem.Equals(lbtBorrowedBooks))
           {
               data = DbDriver.Instance.GetAllBorrowedBooks();
               reportName = "Borrowed Books";
               report = Reports.Reports.BorrowedBooks;
           }
           else if(lstReports.SelectedItem.Equals(lbtExpiredRentals))
           {
               data = DbDriver.Instance.GetAllExpiredRentals();
               reportName = "Expired Rentals";
               report = Reports.Reports.ExpiredRentals;
           }
            Logger.WriteTrace("Generating report - " + reportName,TraceLevel.Info);
            if(data != null)
            {
                lstReportResults.ItemsSource = null;
                lstReportResults.Items.Clear();
                lstReportResults.Foreground = Brushes.White;

                foreach (string item in data)
                {
                    lstReportResults.Items.Add(item);
                }

                lstReportResults.Tag = data;
                lstReportResults.ToolTip = reportName;
                lstReportResults.DataContext = report;
            }
            lblInfo.Content = "\n Report Name: " + reportName + "\n Date : " + DateTime.Now;
        } 

        /// <summary>
        /// Save to file click event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SaveToFileClicked(object sender, RoutedEventArgs e)
        {
            try
            {
                if (lstReportResults.Tag != null && !String.IsNullOrEmpty(lstReportResults.ToolTip.ToString()))
                {
                    Reports.ReportWriter.CreateFile((Reports.Reports)lstReportResults.DataContext);
                    Reports.ReportWriter.WriteToStream(lstReportResults.Tag as List<String>, (Reports.Reports)lstReportResults.DataContext,lstReportResults.ToolTip.ToString());

                    Reports.ReportWriter.OpenFile((Reports.Reports) lstReportResults.DataContext);
                    Logger.WriteTrace("Report saved to file : " + lstReportResults.ToolTip,TraceLevel.Info);
                }
            }
            catch (Exception)
            {
               
            }
        }
       
    }

    /// <summary>
    /// Enum for pasing values for different screens.
    /// </summary>
    public enum Tabs
    {
        Book,
        User
    }
}
